import { CompatibilityTree } from '../compatibility-tree'
import { PackageJson } from 'type-fest'
import semver from 'semver'
import { execSync } from 'child_process'

/**
 * 智能依赖优化器
 * 基于兼容树优化项目依赖
 */
export class DependencyOptimizer {
  constructor(private tree: CompatibilityTree) {}

  /**
   * 分析并优化项目依赖
   * @returns 优化报告
   */
  async analyzeAndOptimize(): Promise<OptimizationReport> {
    const packageJson = this.readPackageJson()
    const dependencyGraph = this.buildDependencyGraph()
    const optimizationPlan = this.generateOptimizationPlan(packageJson, dependencyGraph)
    
    await this.applyOptimizations(optimizationPlan)
    
    return {
      originalDependencies: Object.keys(packageJson.dependencies || {}).length,
      optimizedDependencies: optimizationPlan.keep.length,
      removed: optimizationPlan.remove,
      upgraded: optimizationPlan.upgrade,
      duplicates: optimizationPlan.duplicates
    }
  }

  private readPackageJson(): PackageJson {
    try {
      return JSON.parse(execSync('cat package.json').toString())
    } catch (error) {
      throw new Error('读取package.json失败')
    }
  }

  private buildDependencyGraph(): Record<string, any> {
    try {
      const output = execSync('npm ls --json --all', { 
        encoding: 'utf-8',
        stdio: 'pipe'
      })
      return JSON.parse(output)
    } catch (error) {
      console.warn('构建依赖图时遇到警告:', error.stdout)
      return JSON.parse(error.stdout)
    }
  }

  private generateOptimizationPlan(pkg: PackageJson, graph: any): OptimizationPlan {
    const plan: OptimizationPlan = {
      keep: [],
      remove: [],
      upgrade: [],
      duplicates: []
    }

    // 应用兼容树规则分析依赖
    for (const [name, version] of Object.entries(pkg.dependencies || {})) {
      const compatibleVersions = this.tree.getCompatibleVersions(name)
      
      if (!compatibleVersions) {
        plan.remove.push(name)
        continue
      }

      const optimalVersion = this.findOptimalVersion(version, compatibleVersions)
      if (optimalVersion !== version) {
        plan.upgrade.push({ name, from: version, to: optimalVersion })
      }

      plan.keep.push(`${name}@${optimalVersion}`)
    }

    // 检测重复依赖
    this.detectDuplicates(graph, plan)

    return plan
  }

  private findOptimalVersion(current: string, compatible: string[]): string {
    // 选择满足当前需求的最新稳定版
    return semver.maxSatisfying(compatible, current) || current
  }

  private detectDuplicates(graph: any, plan: OptimizationPlan) {
    const packageMap = new Map<string, Set<string>>()
    
    const traverse = (node: any) => {
      if (node.dependencies) {
        for (const [name, dep] of Object.entries<any>(node.dependencies)) {
          if (!packageMap.has(name)) {
            packageMap.set(name, new Set())
          }
          packageMap.get(name)!.add(dep.version)
          
          traverse(dep)
        }
      }
    }
    
    traverse(graph)
    
    for (const [name, versions] of packageMap) {
      if (versions.size > 1) {
        plan.duplicates.push({
          name,
          versions: Array.from(versions)
        })
      }
    }
  }

  private async applyOptimizations(plan: OptimizationPlan) {
    // 备份原始package.json
    execSync('cp package.json package.json.bak')
    
    try {
      // 移除无用依赖
      if (plan.remove.length > 0) {
        execSync(`npm remove ${plan.remove.join(' ')}`, { stdio: 'inherit' })
      }

      // 升级依赖版本
      for (const { name, to } of plan.upgrade) {
        execSync(`npm install ${name}@${to}`, { stdio: 'inherit' })
      }

      // 处理重复依赖
      for (const { name, versions } of plan.duplicates) {
        const optimalVersion = this.findOptimalVersion(
          versions[0], 
          this.tree.getCompatibleVersions(name) || versions
        )
        execSync(`npm install ${name}@${optimalVersion}`, { stdio: 'inherit' })
      }
    } catch (error) {
      // 恢复备份
      execSync('mv package.json.bak package.json')
      execSync('npm install')
      throw new Error(`优化应用失败，已恢复原始状态: ${error.message}`)
    }
  }
}

interface OptimizationPlan {
  keep: string[]
  remove: string[]
  upgrade: { name: string; from: string; to: string }[]
  duplicates: { name: string; versions: string[] }[]
}

interface OptimizationReport {
  originalDependencies: number
  optimizedDependencies: number
  removed: string[]
  upgraded: { name: string; from: string; to: string }[]
  duplicates: { name: string; versions: string[] }[]
}